package ar.edu.caece.compiler.grammar.factories;

import ar.edu.caece.compiler.grammar.AbstractTokenVisitorFactory;
import ar.edu.caece.compiler.grammar.Recognizer;
import ar.edu.caece.compiler.grammar.TokenRecognizer;
import ar.edu.caece.compiler.grammar.TokenVisitor;
import ar.edu.caece.compiler.grammar.attributes.Attribute;
import ar.edu.caece.compiler.grammar.attributes.AttributeUtils;
import ar.edu.caece.compiler.grammar.attributes.Attribute.OperadorRelacional;
import ar.edu.caece.compiler.grammar.attributes.Attribute.Type;
import ar.edu.caece.compiler.grammar.attributes.tree.NodeCondicion;
import ar.edu.caece.compiler.grammar.attributes.tree.NodeExpr;
import ar.edu.caece.compiler.grammar.attributes.tree.NodeOperacionRelacional;
import ar.edu.caece.compiler.grammar.nonterminals.CONDICION5;
import ar.edu.caece.compiler.grammar.nonterminals.EXPR;
import ar.edu.caece.compiler.grammar.nonterminals.REL;

public class CONDICION5Factory extends AbstractTokenVisitorFactory {

	private static CONDICION5Factory INSTANCE;
	
	private EXPRFactory exprFactory;
	private RELFactory relFactory;
	private TokenVisitor parseEXPR;
	private TokenVisitor parseREL;
	
	public static CONDICION5Factory getInstance() throws Exception {
		if(INSTANCE==null) INSTANCE = new CONDICION5Factory();
		return INSTANCE;
	}
	
	private Initializer initializer = new Initializer() {
		
		@Override
		public void init() throws Exception {
			initializer = NoOperationInitializer.getInstance();
			exprFactory = EXPRFactory.getInstance();
			relFactory = RELFactory.getInstance();
			parseREL = relFactory.makeVisitor();
			parseEXPR = exprFactory.makeVisitor();
		}
	};
	
	private CONDICION5Factory() throws Exception {

	}
	
	@Override
	public TokenVisitor makeChainedVisitor(
			final TokenVisitor siguiente) throws Exception {
		initializer.init();

		return new TokenVisitor() {

			@Override
			public Recognizer defaultCase(TokenRecognizer llamador)
					throws Exception {
				TokenVisitor parseEXPR2 = exprFactory.makeChainedVisitor(siguiente);
				parseEXPR2.addInheritedAttribute(Attribute.TABLA_SIMBOLOS_HEREDADA,getAttribute(Attribute.TABLA_SIMBOLOS_HEREDADA));

				Recognizer result = llamador.execute(parseEXPR2);
				if(result instanceof EXPR) {
					parseEXPR.addInheritedAttribute(Attribute.TABLA_SIMBOLOS_HEREDADA,getAttribute(Attribute.TABLA_SIMBOLOS_HEREDADA));
					
					EXPR expr1 = (EXPR) result;
					REL rel = (REL) nextToken().execute(parseREL);
					EXPR expr2 = (EXPR) nextToken().execute(parseEXPR);
					
					if(! expr2.getAttribute("tipo").equals(expr2.getAttribute("tipo"))) {
						//TODO ERROR
						AttributeUtils.error("Error. Tipos de expresiones no coinciden",llamador);
					}
					
					CONDICION5 condicion5 = new CONDICION5 (expr1, rel,expr2);
					NodeCondicion nodeCondicion = new NodeOperacionRelacional(OperadorRelacional.valueOf(rel.getAttribute("op").toString()),Type.valueOf(expr1.getAttribute("tipo").toString()),
							(NodeExpr) expr1.getAttribute(Attribute.ARBOL_SINTETIZADO),(NodeExpr) expr2.getAttribute(Attribute.ARBOL_SINTETIZADO));
					condicion5.addAttribute(Attribute.ARBOL_SINTETIZADO, nodeCondicion);
					return condicion5;
				} 
				else {
					return result;
				}
			}
			
		};
	
	}
	@Override
	public TokenVisitor makeVisitor() throws Exception {
		initializer.init();

		return new TokenVisitor() {

			@Override
			public Recognizer defaultCase(TokenRecognizer llamador) throws Exception {
				parseEXPR.addInheritedAttribute(Attribute.TABLA_SIMBOLOS_HEREDADA,getAttribute(Attribute.TABLA_SIMBOLOS_HEREDADA));
				
				EXPR expr1 = (EXPR) llamador.execute(parseEXPR);
				REL rel = (REL) nextToken().execute(parseREL);
				EXPR expr2 = (EXPR) nextToken().execute(parseEXPR);
				
				if(! expr2.getAttribute("tipo").equals(expr2.getAttribute("tipo"))) {
					//TODO ERROR
					AttributeUtils.error("Error. Tipos de expresiones no coinciden",llamador);
				}
				
				CONDICION5 condicion5 = new CONDICION5 (expr1, rel,expr2);
				
				NodeCondicion nodeCondicion = new NodeOperacionRelacional(OperadorRelacional.valueOf(rel.getAttribute("op").toString()),Type.valueOf(expr1.getAttribute("tipo").toString()),(NodeExpr) expr1.getAttribute(Attribute.ARBOL_SINTETIZADO),(NodeExpr) expr2.getAttribute(Attribute.ARBOL_SINTETIZADO));
				condicion5.addAttribute(Attribute.ARBOL_SINTETIZADO, nodeCondicion);
				return condicion5;
			}
		};
	}
	
}
